package dmir.reaction.publico.graph.neo4j;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;

import org.apache.commons.cli.CommandLine;
import org.apache.lucene.util.ArrayUtil;
import org.neo4j.graphdb.DynamicRelationshipType;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.RelationshipType;
import org.neo4j.graphdb.Transaction;
import org.neo4j.graphdb.index.Index;
import org.neo4j.graphdb.index.IndexManager;
import org.neo4j.graphdb.index.RelationshipIndex;
import org.neo4j.helpers.collection.MapUtil;
import org.neo4j.kernel.EmbeddedGraphDatabase;
import org.neo4j.tooling.GlobalGraphOperations;
import org.neo4j.unsafe.batchinsert.BatchInserter;
import org.neo4j.unsafe.batchinsert.BatchInserters;

public class LoadNeo4J {
	
	static BatchInserter db;	
	static Connection conn = null;
	
	static enum RelTypes implements RelationshipType {
		CO_OCCURS
	}
	
	public static void main(CommandLine line) throws Exception {
				
		db = BatchInserters.inserter("politica");
		System.out.println("Inserting nodes");
		insertNodes(line.getOptionValue("inputgraph"));
						
		System.out.println("Inserting relationships");
		//conn = MySQLConnection.getConnection("root", "xpto123", null);
		
		if (line.getOptionValue("time").equalsIgnoreCase("true"))
			//insertRelationshipsTime(line.getOptionValue("inputgraph"));
			aggregateMonth(line.getOptionValue("inputgraph"));
		else {
			insertRelationships(line.getOptionValue("inputgraph"));
		}
		
		//conn.close();
		db.shutdown();
		
		System.out.println("Creating indexes");
		createIndexes();
	}
	
	public static void insertNodes(String inputgraph) {
		
		LinkedList<Map<String, Object>> nodes = GraphGephi.getNodes(inputgraph);
		
		for (Map<String, Object> n : nodes) {
			/*
			System.out.println("id: " + n.get("id"));			
			System.out.println("ename: "+ n.get("ename"));
			System.out.println("etype: " + n.get("etype"));
			System.out.println("freq: " + n.get("freq"));
			System.out.println("degree: " + n.get("degree"));
			*/		
			db.createNode(Long.parseLong((String) n.get("id")), n);			
		}
	}
	
	public static String getNewsText(String section, int id) throws Exception {
		
		PreparedStatement preparedStatement = conn.prepareStatement("SELECT title,subtitle,newstext FROM " + section + " WHERE id = ? ");
		preparedStatement.setInt(1, id);
			
		ResultSet resultSet = preparedStatement.executeQuery();
		
		resultSet.next();
		
		String title = resultSet.getString(1);
		String subtitle = resultSet.getString(2);
		String text = resultSet.getString(3);
		
		return title.trim() + '\n' + subtitle.trim() + '\n' + text.trim();
		
	}
	
	public static void createIndexes(){
		
		GraphDatabaseService graphDb = new EmbeddedGraphDatabase( "politica" );
		
		Transaction tx = graphDb.beginTx();
		
		try
		{
			IndexManager index = graphDb.index();
			RelationshipIndex relationshipsIndex = index.forRelationships("cooccurrences", MapUtil.stringMap( IndexManager.PROVIDER, "lucene", "type", "fulltext" ));			
			RelationshipType co_occurs = DynamicRelationshipType.withName( "CO_OCCURS" );
			
			System.out.println("Creating relationships index...");
			
			for ( Relationship rel :  GlobalGraphOperations.at(graphDb).getAllRelationships() ) {
			    if ( rel.isType(co_occurs) ) {
			    	relationshipsIndex.add(rel, "text", rel.getProperty("text"));
			    }
			}
			
			Index<Node> nodesIndex = index.forNodes("nodes_index");
			
			System.out.println("Creating nodes index...");

		    for (Node n : GlobalGraphOperations.at(graphDb).getAllNodes()) {		    	
		    	if (n.getId()==0) {
		    		continue;
		    	}
		    	else {
		    		nodesIndex.add(n, "id", n.getProperty("id"));
		    		nodesIndex.add(n, "ename", n.getProperty("ename"));
		    	}
		    }
		    tx.success();
		}
		
		finally
		{
		    tx.finish();
		}				
		
	}

	public static void insertRelationships(String inputgraph) throws Exception {
		
		LinkedList<MyRelationship> edges = GraphGephi.getEdges(inputgraph);
		
		System.out.println("parsed edges: " + edges.size());
		System.out.println("creating relationships ... ");
		
 		int status = 1;
		for (MyRelationship r : edges) {
			System.out.println( status +"/"+edges.size() );
			RelationshipType co_occurs = DynamicRelationshipType.withName( "CO_OCCURS" );
			
			HashMap<String,Object> properties = new HashMap<String, Object>();
			StringBuffer text = new StringBuffer();			
			
			for (Article a : r.articles) {
				text.append(getNewsText(a.section,a.id)); 
				text.append("\n\n");
				properties.put(a.date+':'+a.time,a.url);
			}
			
			properties.put("text", text.toString());
			properties.put("weight", r.articles.size());
			
			db.createRelationship( r.source, r.target, co_occurs, properties);
			status++;
		}
	}
	
	public static void aggregateMonth(String inputgraph) throws Exception {
		
		LinkedList<MyRelationship> edges = GraphGephi.getEdges(inputgraph);
		
		/* key string is: year + '_' + month */
		HashMap<String,HashMap<String,Set<Article>>> year_month_occurrences = new HashMap<String, HashMap<String,Set<Article>>>();
		
		int status = 1;
		
		System.out.println("Aggregating relationships");
		
		for (MyRelationship r : edges) {
			
			System.out.println( status +"/"+edges.size() );
			
			/* aggregate by year-month */
			for (Article a : r.articles) {
				
				/* which month does it belong ? */ 
				String[] date = a.date.split("-");				
				
				String year_month = String.valueOf(Integer.valueOf(date[0]))+'_'+String.valueOf(Integer.valueOf(date[1]));
				
				if (year_month.startsWith("2000_") || (year_month.startsWith("2011_"))) {
					continue;
				}

				/* which entities co-occur ? */
				String ocurrence = String.valueOf(r.source) + '_' + String.valueOf(r.target);
				
				try {					
					HashMap<String, Set<Article>> occurrence_articles = year_month_occurrences.get(year_month);
					
					try {
						Set<Article> articles = occurrence_articles.get(ocurrence);
						articles.add(a);
					
					} catch (Exception e) {
						Set<Article> articles = new HashSet<Article>();
						articles.add(a);
						occurrence_articles.put(ocurrence, articles);
						year_month_occurrences.put(year_month, occurrence_articles);
					}
										
				} catch (Exception e) {
					HashMap<String, Set<Article>> occurrence_articles = new HashMap<String, Set<Article>>();
					Set<Article> articles = new HashSet<Article>();
					occurrence_articles.put(ocurrence, articles);
					year_month_occurrences.put(year_month, occurrence_articles);					
				}
			}
			status++;
		}
		
		System.out.println("Indexing relationships");
		
		status = year_month_occurrences.keySet().size();
		
		/* create relationship with the all information for one year-month */ 			
		for (String year_month : year_month_occurrences.keySet()) {
			for (String entities : year_month_occurrences.get(year_month).keySet()) {
				
				RelationshipType co_occurs = DynamicRelationshipType.withName( "CO_OCCURS_MONTHLY" );			
				HashMap<String,Object> properties = new HashMap<String, Object>();
				
				Long source = Long.parseLong(entities.split("_")[0]);
				Long target = Long.parseLong(entities.split("_")[1]);
								
				Set<Article> articles = year_month_occurrences.get(year_month).get(entities);
				
				ArrayList<Integer> ids = new ArrayList<Integer>(articles.size());
				ArrayList<Integer> ids_publico = new ArrayList<Integer>(articles.size());
				
				for (Article article : articles) {					
					ids.add(article.id);					
					try {
						ids_publico.add(Integer.valueOf(article.url.split("_")[1]));
					} catch (Exception e) {						
					}					
					//TODO: another property: all the text from the news or top-k works
					//TODO: another property: topics distribution
				}
				
				properties.put("ids_publico", ArrayUtil.toIntArray(ids_publico));
				properties.put("ids", ArrayUtil.toIntArray(ids));
				properties.put("weight", ids.size());
					
				String year = year_month.split("_")[0];
				String month = year_month.split("_")[1];
				
				Calendar cal = new GregorianCalendar(Integer.parseInt(year), Integer.parseInt(month), 1);
				cal.set(Integer.parseInt(year), Integer.parseInt(month), 0, 0, 0, 0);
				
				/* timestamp with milliseconds and day set to last day of month*/
				properties.put("date", cal.getTimeInMillis());
				
				db.createRelationship( source, target, co_occurs, properties);				
			}
		}
	}


	public static void insertRelationshipsTime(String inputgraph) throws Exception {
		
		LinkedList<MyRelationship> edges = GraphGephi.getEdges(inputgraph);
		
		System.out.println("parsed edges: " + edges.size());
		System.out.println("creating relationships ... ");
		
 		int status = 1;
		
 		for (MyRelationship r : edges) {
 			
			System.out.println( status +"/"+edges.size() );
			
			RelationshipType co_occurs = DynamicRelationshipType.withName( "CO_OCCURS" );
			
			for (Article a : r.articles) {
				
				HashMap<String,Object> properties = new HashMap<String, Object>();
				StringBuffer text = new StringBuffer();				
				text.append(getNewsText(a.section,a.id));
				properties.put("text", text.toString());
				properties.put("url", a.url);
				properties.put("id", a.id);
				properties.put("section", a.section);
				
				/*
				<date>2011-02-10</date>
				<time>22:54:00</time>
				*/				
				String[] date = a.date.split("-");
				String[] time = a.time.split(":");				
				
				/* Month value is 0-based. e.g., 0 for January */				
				Calendar cal = new GregorianCalendar(2000, 01, 01);
				cal.clear();				
				cal.set(Integer.valueOf(date[0]), Integer.valueOf(date[1])-1, Integer.valueOf(date[2]), Integer.valueOf(time[0]), Integer.valueOf(time[1]), Integer.valueOf(time[2]));
				properties.put("date", cal.getTimeInMillis());
				
				
				db.createRelationship( r.source, r.target, co_occurs, properties);
			}
			status++;
		}
	}
}













